package com.cloud.mall.conf.gateway;

import cn.hutool.json.JSONUtil;
import com.cloud.mall.enums.ExcEnum;
import com.cloud.mall.enums.HeaderEnum;
import com.cloud.mall.exp.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Author: 梁二东
 * @Date: 2024/12/5 14:26
 * @Description:
 **/
@Component
@Slf4j
@RefreshScope
public class GlobalRoutingFilter implements GlobalFilter, Ordered {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Value("${gateway.routing.type:round}")
    private String routingType;

    private final AtomicInteger atomicInteger = new AtomicInteger(0);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取当前请求的服务 ID
        String serviceId = exchange.getRequest().getHeaders().getFirst(HeaderEnum.serviceId.getHeaderName());
        // 从请求头获取过滤条件
        String grayTag = exchange.getRequest().getHeaders().getFirst(HeaderEnum.grayTag.getHeaderName());
        if (StringUtils.isBlank(serviceId)) {
            String path = exchange.getRequest().getURI().getPath();
            String method = exchange.getRequest().getMethod() == null ? "unknown" : exchange.getRequest().getMethod().name();
            throw CustomException.fail(ExcEnum.METHOD_NOT_ALLOWED.getCode(), "X-Service-ID不能为空", path, method);
        }
        // 如果头信息中没有 X-Gray-Tag 参数
        if (StringUtils.isBlank(grayTag)) {
            return chain.filter(exchange);
        }

        // 根据服务id查询服务列表
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
        log.info("根据serviceId查询服务列表，serviceId={},instances={}", serviceId, JSONUtil.toJsonStr(instances));

        // 根据请求头条件过滤服务实例
        List<ServiceInstance> filteredInstances = instances.stream()
                .filter(instance -> matchHeaderCondition(instance, grayTag))
                .collect(Collectors.toList());

        // 如果没有匹配的实例，则使用默认的实例兜底
        List<ServiceInstance> routingInstances = filteredInstances.isEmpty() ? instances : filteredInstances;

        // 轮询或随机选择一个服务实例
        ServiceInstance instanceToRoute = getNextInstance(serviceId, routingInstances);
        if (instanceToRoute != null) {
            // 构建新的 URI
            URI newUri = instanceToRoute.getUri().resolve(exchange.getRequest().getURI().getPath());
            // 修改请求的 URI
            exchange.getRequest().mutate().uri(newUri).build();
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // ReactiveLoadBalancerClientFilter 10150
        return LOWEST_PRECEDENCE;
    }


    private boolean matchHeaderCondition(ServiceInstance instance, String headerValue) {
        if (StringUtils.isBlank(headerValue)) {
            return true;
        }
        // 根据请求头自定义匹配条件
        return instance.getMetadata().containsValue(headerValue);
    }

    private ServiceInstance getNextInstance(String serviceId, List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            log.warn("{}-服务列表为空，返回null", serviceId);
            return null;
        }
        if ("round".equals(routingType)) {
            int atomicInt = atomicInteger.getAndIncrement();
            if (atomicInt == Integer.MAX_VALUE) {
                atomicInt = 0;
            }
            return instances.get(atomicInt % instances.size());
        }
        // 简单随机实现
        int index = (int) (Math.random() * instances.size());
        return instances.get(index);
    }
}
